package com.heima.search.service.impl;

import com.heima.common.enums.AppHttpCodeEnum;
import com.heima.common.exception.LeadException;
import com.heima.common.threadlocal.UserThreadLocalUtils;
import com.heima.common.util.BeanHelper;
import com.heima.common.util.JsonUtils;
import com.heima.feign.client.ArticleFeign;
import com.heima.model.article.dtos.ArticleDto;
import com.heima.model.search.dtos.ArticleDocument;
import com.heima.model.search.dtos.UserSearchDto;
import com.heima.search.service.RedisService;
import com.heima.search.service.UserSearchService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.action.DocWriteRequest;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.io.IOException;
import java.util.*;

@Slf4j
@Service
public class UserSearchServiceImpl implements UserSearchService {

    @Autowired
    private ArticleFeign articleFeign;

    @Autowired
    private RestHighLevelClient restHighLevelClient;

    private String indexName = "app_info_article";

    @Override
    public List<ArticleDto> findArticleByPage(int page, int size) {

        List<ArticleDto> articleDtoList = articleFeign.findByPage(page, size);

        return articleDtoList;
    }

    /**
     * 导入数据
     * @param list
     */
    @Override
    public void importArticle(List<ArticleDto> list) {


//        把articleDto 拷贝成Es中的数据对象
        List<ArticleDocument> articleDocumentList = BeanHelper.copyWithCollection(list, ArticleDocument.class);
//        创建批量操作对象
        BulkRequest bulkRequest = new BulkRequest();
//        把文章数据批量放入 bulk对象
        for (ArticleDocument doc : articleDocumentList) {
//            创建索引对象
            IndexRequest indexRequest = new IndexRequest(indexName).id(doc.getId().toString());
//            把doc对象转json
            String jsonStr = JsonUtils.toString(doc);
            indexRequest.source(jsonStr,XContentType.JSON);
            bulkRequest.add(indexRequest);
        }
//        使用Es的api导入数据
        try {
            restHighLevelClient.bulk(bulkRequest, RequestOptions.DEFAULT);
        }catch (Exception e){
            log.error("导入Es失败");
            e.printStackTrace();
            throw new LeadException(AppHttpCodeEnum.SERVER_ERROR);
        }
    }

    @Autowired
    private RedisService redisService;
    /**
     * 关键词搜索
     * @param dto
     * @return
     */
    @Override
    public List<ArticleDto> search(UserSearchDto dto) {
        if(StringUtils.isBlank(dto.getSearchWords())){
            log.error("没有关键词");
            throw new LeadException(AppHttpCodeEnum.PARAM_INVALID);
        }
//          创建搜索请求对象
        SearchRequest searchRequest = new SearchRequest(indexName);
//        设置搜索查询条件
//        构造bool查询对象
        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
        boolQuery.must(QueryBuilders.multiMatchQuery(
                dto.getSearchWords(),
                "title","content","labels","authorName"));
        if(dto.getMinBehotTime()!= null){
//            使用最小时间进行发布时间过滤
            boolQuery.filter(QueryBuilders.rangeQuery("publishTime").lt(dto.getMinBehotTime()));
        }
        searchRequest.source().query(boolQuery);
//        分页
        searchRequest.source().from(dto.getFromIndex());
        searchRequest.source().size(dto.getPageSize());
//        排序
        searchRequest.source().sort("publishTime", SortOrder.DESC);
//        高亮  title=辽宁男篮   辽宁  -》 <p>辽宁</p>男篮
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        highlightBuilder.preTags("<p>");
        highlightBuilder.field("title");
        highlightBuilder.postTags("</p>");
        highlightBuilder.requireFieldMatch(false);
        searchRequest.source().highlighter(highlightBuilder);
//        调用Es的Api关键词搜索
        try {
            SearchResponse response = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
//        处理搜索结果
            SearchHit[] hits = response.getHits().getHits();
            List<ArticleDto> list = new ArrayList<>();
            for (SearchHit hit : hits) {
//                获取文档的原始数据
                String jsonStr = hit.getSourceAsString();
//                把json转ArticleDto
                ArticleDto articleDto = JsonUtils.toBean(jsonStr, ArticleDto.class);
//                特殊处理高亮的结果
                Map<String, HighlightField> highlightFields = hit.getHighlightFields();
                if(!CollectionUtils.isEmpty(highlightFields)){
                    HighlightField field = highlightFields.get("title");
                    Text[] fragments = field.getFragments();
                    if(fragments != null && fragments.length>0){
                        String str = fragments[0].toString();
//                        把高亮处理后的title 放入 dto中
                        articleDto.setTitle(str);
                    }
                }
                list.add(articleDto);
            }
//          如果有搜索结果，就保存搜索记录
            if(!CollectionUtils.isEmpty(list)){
//                保存搜索记录
                Integer userId = UserThreadLocalUtils.getUserId();
                redisService.saveSearchWord(userId,dto.getEquipmentId(),dto.getSearchWords());
            }
            return list;
        } catch (IOException e) {
            e.printStackTrace();
            throw new LeadException(AppHttpCodeEnum.SERVER_ERROR);
        }
    }

    @Override
    public void upOrDown(Map<String, Long> map) {
        Long articleId = map.get("articleId");
        Long enable = map.get("enable");
        if(enable==1){
//            根据articleId 查询文章对象
            ArticleDto dto = articleFeign.getByArticleId(articleId);
//            上架，创建索引
            importArticle(Arrays.asList(dto));
        }else{
//            下架,删除索引
            DeleteRequest deleteRequest = new DeleteRequest(indexName).id(articleId.toString());
            try {
                restHighLevelClient.delete(deleteRequest,RequestOptions.DEFAULT);
            } catch (IOException e) {
                log.error("删除索引失败");
                e.printStackTrace();
                throw new LeadException(AppHttpCodeEnum.SERVER_ERROR);
            }
        }
    }
}
